import modules.appconfig
from flask import Flask, jsonify, request, render_template,session,redirect,url_for
from urllib.parse import urlparse
from functools import wraps
import requests
import json
import socket
import random
import modules.log
import modules.db
import os 
import copy
"""
app = Flask(__name__)
# Generate a globally unique address for this node
#node_identifier = str(uuid4()).replace('-', '')
nodes = set()

def register_node(self,address):
    parsed_url=urlparse(address)
    nodes.add(parsed_url.netloc)
"""

from  util.singelMode import singleton
@singleton
class CP2pnet:
    def __init__(self):
        self.__severapp = Flask(__name__)
        # self.__nodes=[]
        self.__address={}
        self.__severapp.config['SECRET_KEY']=os.urandom(24)
        self.initUpdateAddressState()
    def __call__(self):
        return self
    
    def init(self):
        
        pass
    def initAddress(self):
        #self.address['host'] = socket.gethostbyname(socket.gethostname())
        nodes= modules.appconfig.CAppconfig.configFromFile['nodes']
        for node in nodes:
            host=node['host']
            port=node['port']
            if host!=self.address['host'] or port!= self.address['port']:
                hostAndport=f'{ host}:{port}'
                if self.ifIsOnline(hostAndport):
                    modules.db.Sqlite3Db.node_add(hostAndport)
                # self.__nodes.append(f'{ host}:{port}')
        return True
    def initUpdateAddressState(self):
        self.address['host'] = modules.appconfig.CAppconfig.configFromFile['host']
        self.address['port'] = modules.appconfig.CAppconfig.configFromFile['port']
        self.address['addrType'] = 1
        result=modules.db.Sqlite3Db.node_findByState(1)
        nodes=[]
        if result!=None and  len(result)>0:
            for row in result:
                if self.ifIsOnline(row[0]):
                    modules.db.Sqlite3Db.node_updateStateByhostAndport(row[0],1)
                    nodes.append(row[0])
                else:
                    modules.db.Sqlite3Db.node_delByhostAndport(row[0])
        result=modules.db.Sqlite3Db.node_findByState()
        if result!=None and  len(result)>0:
            for row in result:
                if row[0] not in nodes:
                    if not self.ifIsOnline(row[0]):
                        modules.db.Sqlite3Db.node_updateStateByhostAndport(row[0],1)
        else:
            self.initAddress()

    @property
    def address(self):
        return self.__address

    @property
    def severapp(self):
        return self.__severapp
    
    # @property
    # def knowNodes(self):
    #     return self.__nodes
    @staticmethod
    def register_node(address):
        # parsed_url=urlparse(address)
        # modules.db.Sqlite3Db.node_add(parsed_url.netloc)
        modules.db.Sqlite3Db.node_add(address)
        # self.__nodes.append(parsed_url.netloc) 
    
    def startSever(self,host="",port=0):
        if host=="" or port == 0:
             self.severapp.run(self.address['host'],self.address['port'])
        else:
            self.severapp.run(host, port)
    def runtest(self,port=8080):
        self.severapp.run(port)
         
        

    def broadcast(self,broadcasttype,data,excludeNode='',node=''):
        """
        data 为pyhton obejct 
        """
        """
        data={
            "sender" :self.sender.get(),
            "recipient":self.recieve.get(),
            "amount":self.amount.get()
        }
        """
        response = None
        headers = {'content-type': "application/json"}
        if node=='':
            node = self.getRandomNode(excludeNode)
        if node != "":
            if broadcasttype == "tx":
                url = f'http://{node}/transaction'
            elif broadcasttype == "blk":
                url = f'http://{node}/block'
            elif broadcasttype == "chainInfo":
                url = f'http://{node}/chainInfo'
            try:
                host =self.address['host'] 
                port = self.address['port']
                data={
                    'addrFrom':f'{host}:{port}',
                    'data':data
                }
                response =requests.post(url, data = json.dumps(data), headers = headers,timeout=(3.05, 27))
            except:
                modules.log.Clog.logger.info(f'broadcast时连接{url}失败。。。。。。。。。。。')
            else:
                if response.status_code == 200:
                    return response
                   
        return response
    def sycNodesInfoWithotherNode(self,node=""):
        if node != "":
            result=modules.db.Sqlite3Db.node_findByState()
            if result!=None and  len(result)>0:
                nodes=[]
                for row in result:
                        a={
                            'hostAndport':row[0],
                            'state':0
                        }
                        nodes.append(a)
                host =self.address['host'] 
                port = self.address['port']
                option = {
                    'method': 'POST',
                    'api': '/netNodesInfo',
                    'data':{
                        'addrFrom':f'{host}:{port}',
                        'data':nodes
                    }
                } 
                response=self.getFromPeer(node,option)
                if response!='' and response.status_code==200:
                    foreignerAddr =  response.json()['addrFrom']
                    fromKowNodes= response.json()['data']
                    a={
                        'hostAndport':f'{host}:{port}',
                        'state':0
                    }
                    if a in fromKowNodes:
                        del fromKowNodes[fromKowNodes.index(a)]
                    modules.db.Sqlite3Db.node_addNodes(fromKowNodes)
    def getFromPeer(self,node,option):
        """
        option is python dict
        such as:
        option = {
            'method': 'POST',
            'api': '/block'
            'data':{}
        } 
        """
        #host = node["host"]
        #port = node["port"]
        api = option['api']
        method = option['method'].lower()
        url= f'http://{node}{api}'
        requireResponse=""
        if 'data' in option:
            headers = {'content-type': "application/json"}
            try:
                response = eval(f'requests.{method}')(url,data = json.dumps(option['data']),headers = headers,timeout=(3.05, 27))
            except:
                modules.log.Clog.logger.info(f'getFromPeer时连接{url}失败。。。。。。。。。。。')
            else:
                requireResponse = response
                    
        else:
            try:
                response = eval(f'requests.{method}')(url,timeout=(3.05, 27))
            except:
                modules.log.Clog.logger.info(f'getFromPeer时连接{url}失败。。。。。。。。。。。')
            else:
                requireResponse = response
        
        return requireResponse 
    def ifIsOnline(self,node):
        try:
            response = requests.get(f'http://{node}/isOnline',timeout=(3.05, 27))
        except:
            return False
        else:
            if response.status_code==200:
                self.sycNodesInfoWithotherNode(node)
                return True
            else:
                return False


    def getRandomNode(self,excludeNode=''):
        result=modules.db.Sqlite3Db.node_randomFindByState(excludeNode=excludeNode)
        if result!=None and len(result)==1:
            while result!=None and len(result)==1:
                if self.ifIsOnline(result[0][0]):
                    return result[0][0]
                else:
                    modules.db.Sqlite3Db.node_updateStateByhostAndport(result[0][0],1)
                    result=modules.db.Sqlite3Db.node_randomFindByState(excludeNode=excludeNode)
            return ""
        else:
            result=modules.db.Sqlite3Db.node_findByState(1)
            nodes=[]
            if result!=None and  len(result)>0:
                for row in result:
                    if self.ifIsOnline(row[0]):
                        modules.db.Sqlite3Db.node_updateStateByhostAndport(row[0],0)
                        nodes.append(row[0])
                if len(nodes)>0:
                    n = random.randint(0,len(nodes)-1)
                    return nodes[n]
                else:
                    return ""
            else:
                return ""
    @staticmethod
    def getKnowNodesNumber():
        result=modules.db.Sqlite3Db.node_findByState()
        if result!=None and  len(result)>0:
            return len(result)
        else:
            return 0

    
    @staticmethod
    # 登录限制修饰器
    def login_required(func):
        @wraps(func)
        def wrapper(*args,**kwargs):
            if session.get('user_id'):
                return func(*args,**kwargs)
            else:
                return redirect(url_for('login'))
        return wrapper



          

    










